﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net.Sockets;
using ircCommunication;

//misto Console to predelej na System.Diagnostics.Debug - to by mohlo jit replacem
//pomoci System.Diagnostics.Debug.Listeners.Add si zaregistruj vlastni tridu, ktera bude zpracovavat debug vyisy

namespace ircBot
{

    public enum dbgLevel : uint
    {
        OFF,
        LOW,
        MEDIUM,
        HIGH
    }

    public enum botConStatus : uint
    {
        DISCONNECTED,       // not even connected to IRC server
        CONNECTING,         // waiting for NOTICE AUTH message to send PASS, NICK, USER commands
        CONNECTED,          // waiting for MODE message to send JOIN command
        ONCHANNEL           // on channel and communicating 
    }

    public enum pmType : uint
    {
        NONE,
        QUIT,
        LEARN,
        QUOTE,
        UNLEARN,
        DELQUOTE,
        ATTACK,
        KILL,
        REGISTER,
        ADDUSER,
        DELUSER,
        WHOAMI,
        DOWN,
        UP,
        PASSWD,
        PROMOTE,
        DEMOTE,
        UPMSG,
        DOWNMSG,
        RAW,
        HELP,
        LINFO,
        LOPEN,
        LCLOSE,
        IGNOREKZ,
        UNIGNOREKZ,
        LIGNORE,
        LUNIGNORE,
        LCHANCE,
        STATS,
        LLIST,
        SEEN
    }

    public enum ircUserPriv : uint
    {
        UNKNOWN,
        NORMAL,
        FRIEND,
        BOSS,
        MASTER
    }

    public struct ircUser
    {
        //public String id;
        public String nick;
        public String pwd;
        public ircUserPriv level;
    }

    public struct pmCommand
    {
        public pmType type;
        public ircUserPriv reqLevel;
        public String phrase;
    }

    public struct ircPrivMsg
    {

        public ircUser sender;     // who sent it

        public bool isCmd;         // is it a predefined command?
        public pmCommand cmd;      // command info (ignore if isCmd is false)
        public int prmNum;         // number of command params (ignore if isCmd is false, value = 0)
        public String[] prms;      // command params (ignore if isCmd is false, value = null)

        public bool pmToBot;       // is it a PM directly to the bot?
        public bool addressed;     // is vocative used?
        
    }

    public class learnedItem
    {
        //public String phrase;
        public int ignoreChance;
        public int locked;
        public String author;
        public List<String> responses;
        private static Random rnd;

        public learnedItem()
        {
            this.responses = new List<string>();
            this.locked = 0;
            rnd = new Random(15703);
        }

        public void addResponse(String r)
        {
            this.responses.Add(r);
        }

        public bool respond()
        {
            return (rnd.Next(100) > this.ignoreChance);
        }

        public String getResponse()
        {

            return this.responses[rnd.Next(this.responses.Count - 1)];

        }

        public void delResponse(String r)
        {
            this.responses.Remove(r);
        }

        public int numResponses
        {

            get
            {
                return this.responses.Count;
            }
            protected set {}

        }
    
    }

    public class ircBot
    {
        private dbgLevel debugLevel = dbgLevel.HIGH;
        // buffer for received messages
        protected MessageBuffer<ircMessage> chat = new MessageBuffer<ircMessage>();
        // buffer for all new messages
        protected Queue<ircMessage> newMsg = new Queue<ircMessage>(100);
        // our tcp client
        protected TcpClient client = new TcpClient();
        // stream where to write and read
        protected Stream netStream;
        // connection status
        botConStatus conStatus = botConStatus.DISCONNECTED;
        // should the program run?
        public bool run = true;

        // known commands
        protected List<pmCommand> pmCmds = new List<pmCommand>();
        // all known users (account database)
        protected Dictionary<String, ircUser> accdb = new Dictionary<String, ircUser>();
        // registered users
        protected Dictionary<String, ircUser> users = new Dictionary<String, ircUser>();
        // .learn -ed thingies
        protected Dictionary<String, learnedItem> memory = new Dictionary<String, learnedItem>();
        // names on channel
        // created when NICK, JOIN, PART, KICK or QUIT message is detected, or when NAMES list is acquired
        protected List<String> names = new List<String>();
        // seen list
        protected Dictionary<String, DateTime> seenList = new Dictionary<String, DateTime>();
        // cipher password
        protected const String cipherPass = "c1ph3rp4ssw0rd";
        
        // server and port for connection
        protected String server = "irc.quakenet.org"; //213.48.150.3
        protected int port = 6667;
        // channel to join
        protected String channel = "#gameparkl2";

        // bot nickname
        protected String botNick = "Arthemis";
        
        // server status
        protected bool serverStatus = true;
        protected String upMessage;
        protected String downMessage;

        // ignore lowest rights users?
        protected bool ignoreKZ = false;

        // Random generator
        Random rnd;

        public void communicate()
        {            
            ircMessage rcvd;
            ircMessage temp = new ircMessage();

            this.receive();

            while (this.newMsg.Count > 0)
            {

                rcvd = this.newMsg.Dequeue();
                this.chat.Add(rcvd);

                switch (conStatus)
                {
                    case botConStatus.DISCONNECTED:
                        {

                            if (rcvd.type == iMsgType.NOTICE)
                            {
                                temp.Create("PASS heslo\r\n");
                                send(temp);
                                temp.Create("NICK " + this.botNick + "\r\n");
                                send(temp);
                                temp.Create("USER " + this.botNick + " . . :" + this.botNick + "\r\n");
                                send(temp);
                                this.conStatus = botConStatus.CONNECTING;
                            }

                            break;
                        }
                    case botConStatus.CONNECTING:
                        {
                            if (rcvd.cNumber == 221) this.conStatus = botConStatus.CONNECTED;
                            break;
                        }
                    case botConStatus.CONNECTED:
                        {
                            temp.CreatePrivMsg("Q@Cserve.quakenet.org", "AUTH Arthemis dabot834\r\n");
                            send(temp);
                            temp.Create("JOIN " + this.channel + "\r\n");
                            send(temp);
                            this.conStatus = botConStatus.ONCHANNEL;
                            break;
                        }
                    case botConStatus.ONCHANNEL:
                        {

                            break;
                        }
                    default: { break; }
                }

                if (rcvd.type == iMsgType.PING)
                {
                    
                    rcvd.ReplyToPing();
                    send(rcvd);

                }
                else
                {
                    replyToMsg(rcvd);
                }
            
            }

        }

        public void replyToMsg(ircMessage msg)
        {

            switch (msg.type)
            {

                case iMsgType.PRIVMSG:
                    {
                        privMsg(msg);
                        break;
                    }
                case iMsgType.NICK:
                case iMsgType.KICK:
                case iMsgType.PART:
                case iMsgType.JOIN:
                case iMsgType.QUIT: { updateNames(msg); break; }
                case iMsgType.NUMERIC_REPLY: 
                    { 
                        if (msg.cNumber == 353)
                            updateNames(msg);
                        break; 
                    }

            }

        }

        public void updateNames(ircMessage msg)
        {           

            switch (msg.type)
            {
                case iMsgType.NICK:
                    {
                        //:Ascaria!~Ascaria@Thalestris.users.quakenet.org NICK :blable
                        // remove someone from the list and add his new name

                        if (users.ContainsKey(msg.pName))
                        {
                            ircUser tmp = users[msg.pName];
                            users.Remove(msg.pName);
                            users.Add(msg.parameters[msg.prmCount - 1], tmp);
                            Console.WriteLine("User list updated.");
                        }

                        if (names.Remove(msg.pName))
                        {
                            names.Add(msg.parameters[msg.prmCount - 1]);
                            Console.WriteLine("{0} succesfully removed from my names list and added as {1}.", msg.pName, msg.parameters[msg.prmCount - 1]);
                        }
                        else
                        {
                            names.Add(msg.parameters[msg.prmCount - 1]);
                            Console.WriteLine("ERROR! Could not find {0} in my names list, but added {1}. My names list is now wrong!", msg.pName, msg.parameters[msg.prmCount - 1]);
                        }
                        // Update SEEN list
                        if (seenList.ContainsKey(msg.parameters[msg.prmCount - 1].ToLower()))
                        {
                            seenList.Remove(msg.parameters[msg.prmCount - 1].ToLower());
                        }

                        seenList.Add(msg.parameters[msg.prmCount - 1].ToLower(), DateTime.Now);

                        dumpSeenList();
                        
                        break;
                    }
                case iMsgType.KICK:
                    {
                        // :SilverMan!~peta@82.100.0.62 KICK #fufufu Ascaria :Ascaria
                        // remove someone from the list

                        for (int i = 1; i < (msg.prmCount - 1); i++)
                        {

                            if (users.ContainsKey(msg.parameters[i]))
                            {
                                users.Remove(msg.parameters[i]);                                
                            }

                            if (names.Remove(msg.parameters[i]))
                            {
                                Console.WriteLine("{0} succesfully removed from my names list.", msg.parameters[i]);
                            }
                            else
                            {
                                Console.WriteLine("ERROR! Could not find {0} in my names list. My names list is now wrong!", msg.parameters[i]);
                            }
                        }
                        break;
                    }
                case iMsgType.PART:
                    {
                        //:blable!~Ascaria@Thalestris.users.quakenet.org PART #fufufu
                        // remove someone from the list and add his new name

                        if (users.ContainsKey(msg.pName))
                        {
                            users.Remove(msg.pName);
                        }

                        if (names.Remove(msg.pName))
                        {
                            Console.WriteLine("{0} succesfully removed from my names list.", msg.pName);
                        }
                        else
                        {
                            Console.WriteLine("ERROR! Could not find {0} in my names list. My names list is now wrong!", msg.pName);
                        }

                        // Update SEEN list
                        if (seenList.ContainsKey(msg.pName.ToLower()))
                        {
                            seenList.Remove(msg.pName.ToLower());
                        }

                        seenList.Add(msg.pName.ToLower(), DateTime.Now);

                        dumpSeenList();
                        
                        break;
                    }
                case iMsgType.JOIN:
                    {
                        //:Ascaria!~Ascaria@Thalestris.users.quakenet.org JOIN #fufufu
                        // add someone to the list

                        // do not add myself
                        if (msg.pName.Contains(botNick)) break;

                        names.Add(msg.pName);
                        Console.WriteLine("{0} added to my names list.", msg.pName);

                        // Update SEEN list
                        if (seenList.ContainsKey(msg.pName.ToLower()))
                        {
                            seenList.Remove(msg.pName.ToLower());
                        }

                        seenList.Add(msg.pName.ToLower(), DateTime.Now);

                        dumpSeenList();

                        break;
                    }
                case iMsgType.QUIT:
                    {
                        //:blable!~Ascaria@Thalestris.users.quakenet.org QUIT :Bye.
                        // remove someone from the list and add his new name

                        if (users.ContainsKey(msg.pName))
                        {
                            users.Remove(msg.pName);
                        }

                        if (names.Remove(msg.pName))
                        {
                            Console.WriteLine("{0} succesfully removed from my names list.", msg.pName);
                        }
                        else
                        {
                            Console.WriteLine("ERROR! Could not find {0} in my names list. My names list is now wrong!", msg.pName);
                        }

                        // Update SEEN list
                        if (seenList.ContainsKey(msg.pName.ToLower()))
                        {
                            seenList.Remove(msg.pName.ToLower());
                        }

                        seenList.Add(msg.pName.ToLower(), DateTime.Now);

                        dumpSeenList();

                        break;
                    }
                case iMsgType.NUMERIC_REPLY:
                    {
                        // :xs4all.nl.quakenet.org 353 Arthemis = #fufufu :Arthemis @SilverMan @ErDraug @Ascaria @Q @MieraK
                        // empty the list
                        // add all to the list
                        
                        names.Clear();

                        String[] delim = new String[] { " " };
                        String[] cNames = msg.parameters[msg.prmCount - 1].Split(delim, StringSplitOptions.RemoveEmptyEntries);

                        Console.WriteLine("Acquired NAMES list. The following users were added to my names list:");

                        String tmp;

                        foreach (String s in cNames)
                        {
                            // do not add myself
                            if (s.Contains(botNick)) continue;
                            // add nicknames without op sign
                            tmp = s.Replace("@", "").Replace("+", "");
                            names.Add(tmp);
                            Console.WriteLine("{0}", tmp);
                        }

                        break;
                    }

            }
        }

        public void searchMemory(ircMessage msg, ircPrivMsg msgInfo)
        {
            
            // set the default target of all messages
            String target = this.channel;
            // reply message
            ircMessage reply = new ircMessage();

            if (msgInfo.pmToBot) target = msg.pName;

            String searchedPhrase = msg.parameters[msg.prmCount - 1].ToLower(); 
            
            if (memory.ContainsKey(searchedPhrase)) {

                    
                    if (memory[searchedPhrase].respond())
                    {
                        reply.CreatePrivMsg(target, memory[searchedPhrase].getResponse());
                        send(reply);
                        Console.WriteLine("Reply from memmory found and sent.");
                    }
                    else
                    {
                        Console.WriteLine("Reply from memmory found but not sent.");
                    }

            }
                    
        }

        public void privMsg(ircMessage msg)
        {

            ircPrivMsg msgInfo;

            ircMessage reply = new ircMessage();

            msgInfo = analyzePrivMsg(msg);

            // return if ignoreKZ is true and access level is below FRIEND
            // only exception is the register command
            if (ignoreKZ == true)
            {
                if (msgInfo.cmd.type != pmType.REGISTER)
                {
                    if (msgInfo.sender.level == ircUserPriv.UNKNOWN || msgInfo.sender.level == ircUserPriv.NORMAL) return;
                }
            }

            // if it's not a predefined command, look for phrases in memmory
            // if none found, look for insults and nice words
            // if none found, try a random reply
            if (!msgInfo.isCmd)
            {
                if (msgInfo.addressed)
                {
                    // dialogues
                }
                else
                {
                    // simple answers
                    searchMemory(msg, msgInfo);
                }
                return;
            }

            if ((uint)msgInfo.sender.level < (uint)msgInfo.cmd.reqLevel)
            {
                Console.WriteLine("Acquired " + msgInfo.cmd.type.ToString() + " command - ignored due to insufficient privileges of sender.");
                return;
            }

            // set the default target of all messages
            String target = this.channel;

            switch (msgInfo.cmd.type)
            {
                case pmType.QUIT:
                    {
                        
                        reply.Create("QUIT :Maybe i'm not human, but you can still love me!\r\n");
                        send(reply);
                        this.run = false;
                        Console.WriteLine("Acquired QUIT command - exiting.");
                        System.Threading.Thread.Sleep(2000);
                        
                        break;
                    }
                case pmType.WHOAMI:
                    {

                        target = msg.pName;
                                                
                        switch (msgInfo.sender.level) 
                        {
                            case ircUserPriv.NORMAL : { reply.Create("PRIVMSG " + target + " :You are known as " + msgInfo.sender.nick + ". Access level: 1\r\n"); break; }
                            case ircUserPriv.FRIEND: { reply.Create("PRIVMSG " + target + " :You are known as " + msgInfo.sender.nick + ", my friend. Access level: 2\r\n"); break; }
                            case ircUserPriv.BOSS: { reply.Create("PRIVMSG " + target + " :You are known as " + msgInfo.sender.nick + ", my boss. Access level: 3\r\n"); break; }
                            case ircUserPriv.MASTER: { reply.Create("PRIVMSG " + target + " :You are known as " + msgInfo.sender.nick + ", my master. Access level: 4\r\n"); break; }
                            default: { reply.Create("PRIVMSG " + target + " :I don't know you, login or register please. You have no access rights.\r\n"); break; }
                        }

                        send(reply);
                        Console.WriteLine("Acquired WHOAMI command - reply sent.");
                        
                        break;
                    }
                case pmType.ATTACK:
                    {

                        // if we acquired this command through a pm, send reply through pm
                        // otherwise keep the default (channel we are on)
                        if (msgInfo.pmToBot) target = msg.pName;

                        if (msgInfo.addressed)
                        {
                            if (msgInfo.prmNum > 2)
                            {
                                reply.Create("PRIVMSG " + target + " :" + (char)(1) + "ACTION shoots " + msgInfo.prms[2] + "! Wow, headshot!" + (char)(1) + "\r\n");
                            }
                            else
                            {
                                reply.Create("PRIVMSG " + target + " :" + (char)(1) + "ACTION shoots " + names[rnd.Next(names.Count - 1)] + "! Wow, headshot!" + (char)(1) + "\r\n");
                            }

                            send(reply);
                            Console.WriteLine("Acquired ATTACK command - reply sent.");
                        }
                        else if (!msgInfo.addressed)
                        {
                            if (msgInfo.prmNum > 1)
                            {
                                reply.Create("PRIVMSG " + target + " :" + (char)(1) + "ACTION shoots " + msgInfo.prms[1] + "! Wow, headshot!" + (char)(1) + "\r\n");
                            }
                            else
                            {
                                reply.Create("PRIVMSG " + target + " :" + (char)(1) + "ACTION shoots " + names[rnd.Next(names.Count - 1)] + "! Wow, headshot!" + (char)(1) + "\r\n");
                            }

                            send(reply);
                            Console.WriteLine("Acquired ATTACK command - reply sent.");
                        }
                        else
                        {
                            send(reply);
                            Console.WriteLine("Acquired ATTACK command - no reply sent, insufficient parameters.");
                        }
                        
                        break;
                    }
                case pmType.UP:
                    {
                        reply.Create("TOPIC " + channel + " :" + upMessage + "\r\n");
                        send(reply);
                        serverStatus = true;
                        Console.WriteLine("Acquired UP command - reply sent, status updated.");
                        break;
                    }
                case pmType.DOWN:
                    {
                        reply.Create("TOPIC " + channel + " :" + downMessage + "\r\n");
                        send(reply);
                        serverStatus = false;
                        Console.WriteLine("Acquired DOWN command - reply sent, status updated.");
                        break;
                    }
                case pmType.LEARN:
                    {
                        if (msgInfo.pmToBot) target = msg.pName;

                        switch (addToMemory(msgInfo, msg.pName))
                        {
                            case 0:
                                {
                                    reply.Create("PRIVMSG " + target + " :Learned.\r\n");
                                    send(reply);
                                    Console.WriteLine("Acquired LEARN command - reply sent, added to memmory.");
                                    dumpMemory();
                                    break;
                                }
                            case 1:
                                {
                                    reply.Create("PRIVMSG " + target + " :You forgot to specify how should i react to that keyword.\r\n");
                                    send(reply);
                                    Console.WriteLine("Acquired LEARN command - reply sent, nothing learned - too few parameters.");
                                    break;
                                }
                            case 2:
                                {
                                    reply.Create("PRIVMSG " + target + " :That's too long, i cannot learn it. (keyword max. 50 chars, value max. 400 chars)\r\n");
                                    send(reply);
                                    Console.WriteLine("Acquired LEARN command - reply sent, nothing learned - too long key or value.");
                                    break;
                                }
                            case 3:
                                {
                                    reply.Create("PRIVMSG " + target + " :Keyword is too short, sorry.\r\n");
                                    send(reply);
                                    Console.WriteLine("Acquired LEARN command - reply sent, nothing learned - too short key.");
                                    break;
                                }
                            case 4:
                                {
                                    reply.Create("PRIVMSG " + target + " :Cannot add, learn keyword is locked.\r\n");
                                    send(reply);
                                    Console.WriteLine("Acquired LEARN command - reply sent, nothing learned - learn locked.");
                                    break;
                                }
                        }
                        
                        break;
                    }
                case pmType.UNLEARN:
                    {

                        if (msgInfo.pmToBot) target = msg.pName;

                        switch (remFromMemory(msgInfo))
                        {
                            case 0:
                                {
                                    reply.Create("PRIVMSG " + target + " :Forgotten.\r\n");
                                    send(reply);
                                    Console.WriteLine("Acquired UNLEARN command - reply sent, removed from memmory.");
                                    dumpMemory();
                                    break;
                                }
                            case 1:
                                {
                                    reply.Create("PRIVMSG " + target + " :No such keyword.\r\n");
                                    send(reply);
                                    Console.WriteLine("Acquired UNLEARN command - reply sent, keyword not found.");
                                    break;
                                }
                            case 2:
                                {
                                    reply.Create("PRIVMSG " + target + " :Cannot unlearn, keyword is locked.\r\n");
                                    send(reply);
                                    Console.WriteLine("Acquired UNLEARN command - reply sent, cannot unlearn, keyword locked.");
                                    break;
                                }
                        }

                        break;
                    }
                case pmType.REGISTER:
                    {
                        // ignore, if it's not a pm or it is addressed
                        if (!msgInfo.pmToBot || msgInfo.addressed) break;

                        // format: .register <username> <password>
                        target = msg.pName;
                        
                        if (msgInfo.prmNum < 3)
                        {
                            reply.CreatePrivMsg(target, "Not enough parameters.");
                            send(reply);
                            break;
                        }

                        if (users.ContainsKey(msg.pName))
                        {
                            reply.CreatePrivMsg(target, "Already registered!");
                            send(reply);
                            break;
                        }
                        
                        if (accdb.ContainsKey(msgInfo.prms[1]))
                        {

                            // cipher the password and compare
                            String cpwd = RijndaelSimple.quickEncrypt(cipherPass, msgInfo.prms[2]); 

                            if (accdb[msgInfo.prms[1]].pwd.Equals(cpwd))
                            {
                                // user exists in acc db and password is correct
                                users.Add(msg.pName, accdb[msgInfo.prms[1]]);
                                reply.CreatePrivMsg(target, "Succesfully registered.");

                            }
                            else
                            {
                                // user exists but password is wrong
                                reply.CreatePrivMsg(target, "Incorrect password.");
                            }

                        }
                        else
                        {
                            // no such user
                            reply.CreatePrivMsg(target, "I don't know anyone with that name, sorry.");
                        }

                        send(reply);

                        break;
                    }
                case pmType.ADDUSER:
                    {
                        // ignore, if it's not a pm or it is addressed
                        if (!msgInfo.pmToBot || msgInfo.addressed) break;

                        target = msg.pName;

                        if (msgInfo.prmNum < 4)
                        {
                            reply.CreatePrivMsg(target, "Not enough parameters.");
                            send(reply);
                            break;
                        }

                        ircUser nUser = new ircUser();
                        nUser.nick = msgInfo.prms[1];
                        // cipher the password and compare
                        String cpwd = RijndaelSimple.quickEncrypt(cipherPass, msgInfo.prms[2]);
                        nUser.pwd = cpwd;

                        try
                        {
                            nUser.level = (ircUserPriv)(Convert.ToUInt32(msgInfo.prms[3]));
                            // master cannot be created!
                            if (nUser.level == ircUserPriv.MASTER) throw new ArgumentOutOfRangeException();
                        }
                        catch (Exception)
                        {
                            reply.CreatePrivMsg(target, "Wrong access rights.");
                            send(reply);
                            break;
                        }

                        accdb.Add(msgInfo.prms[1], nUser);
                        
                        reply.CreatePrivMsg(target, "User succesfully added.");
                        send(reply);

                        dumpAccDB();

                        break;
                    }
                case pmType.DELUSER:
                    {
                        // ignore, if it's not a pm or it is addressed
                        if (!msgInfo.pmToBot || msgInfo.addressed) break;

                        target = msg.pName;

                        if (msgInfo.prmNum < 2)
                        {
                            reply.CreatePrivMsg(target, "Not enough parameters.");
                            send(reply);
                            break;
                        }

                        if (accdb.ContainsKey(msgInfo.prms[1]))
                        {

                            if (accdb[msgInfo.prms[1]].level == ircUserPriv.MASTER)
                            {
                                reply.CreatePrivMsg(target, "User with master access cannot be removed.");
                                send(reply);
                                return;
                            }

                            accdb.Remove(msgInfo.prms[1]);

                            if (users.ContainsKey(msgInfo.prms[1])) users.Remove(msgInfo.prms[1]);

                            reply.CreatePrivMsg(target, "User succesfully removed.");
                        }
                        else
                        {
                            reply.CreatePrivMsg(target, "No such user.");
                        }

                        send(reply);
                        dumpAccDB();

                        break;
                    }
                case pmType.PASSWD:
                    {
                        // ignore, if it's not a pm or it is addressed
                        if (!msgInfo.pmToBot || msgInfo.addressed) break;
                                                
                        target = msg.pName;

                        if (msgInfo.prmNum < 4)
                        {
                            reply.CreatePrivMsg(target, "Not enough parameters.");
                            send(reply);
                            break;
                        }

                        if (accdb.ContainsKey(msgInfo.prms[1]))
                        {

                            // cipher the password and compare
                            String cpwd = RijndaelSimple.quickEncrypt(cipherPass, msgInfo.prms[2]);

                            if (accdb[msgInfo.prms[1]].pwd.Equals(cpwd))
                            {
                                // user exists in acc db and password is correct
                                ircUser nUser = accdb[msgInfo.prms[1]];

                                String rmKey = "invalid nickname";

                                if (users.ContainsValue(nUser))
                                {

                                    Dictionary<String, ircUser>.Enumerator e = users.GetEnumerator();
                                                                        
                                    do
                                    {
                                        if (e.Current.Value.Equals(nUser)) { rmKey = e.Current.Key; break; }

                                    } while (e.MoveNext());

                                    if (!rmKey.Equals("invalid nickname")) users.Remove(rmKey);

                                }

                                cpwd = RijndaelSimple.quickEncrypt(cipherPass, msgInfo.prms[3]);
                                nUser.pwd = cpwd;
                                accdb.Remove(nUser.nick);
                                accdb.Add(nUser.nick, nUser);
                                if (!rmKey.Equals("invalid nickname")) users.Add(rmKey, nUser);
                                                                
                                reply.CreatePrivMsg(target, "Password succesfully changed.");

                            }
                            else
                            {
                                // user exists but password is wrong
                                reply.CreatePrivMsg(target, "Incorrect password.");
                            }

                        }
                        else
                        {
                            reply.CreatePrivMsg(target, "No such user.");
                        }

                        send(reply);
                        dumpAccDB();

                        break;
                    }
                case pmType.PROMOTE:
                    {
                        if (!msgInfo.pmToBot || msgInfo.addressed) break;

                        target = msg.pName;

                        if (msgInfo.prmNum < 2)
                        {
                            reply.CreatePrivMsg(target, "Not enough parameters.");
                            send(reply);
                            break;
                        }

                        if (accdb.ContainsKey(msgInfo.prms[1]))
                        {

                            if (accdb[msgInfo.prms[1]].level == ircUserPriv.BOSS || accdb[msgInfo.prms[1]].level == ircUserPriv.MASTER)
                            {
                                reply.CreatePrivMsg(target, "Cannot promote to MASTER. Forbidden.");
                                send(reply);
                                break;
                            }

                            // user exists in acc db
                            ircUser nUser = accdb[msgInfo.prms[1]];

                            String rmKey = "invalid nickname";

                            if (users.ContainsValue(nUser))
                            {

                                Dictionary<String, ircUser>.Enumerator e = users.GetEnumerator();
                                
                                do
                                {
                                    if (e.Current.Value.Equals(nUser)) { rmKey = e.Current.Key; break; }

                                } while (e.MoveNext());

                                if (!rmKey.Equals("invalid nickname")) users.Remove(rmKey);

                            }

                            uint tmpLevel = (uint)nUser.level;
                            tmpLevel++;
                            nUser.level = (ircUserPriv)tmpLevel;
                            accdb.Remove(nUser.nick);
                            accdb.Add(nUser.nick, nUser);
                            if (!rmKey.Equals("invalid nickname")) users.Add(rmKey, nUser);

                            reply.CreatePrivMsg(target, "User succesfully promoted to " + nUser.level.ToString() + ".");

                        }
                        else
                        {
                            reply.CreatePrivMsg(target, "No such user.");
                        }

                        send(reply);
                        break;

                    }

                case pmType.DEMOTE:
                    {
                        if (!msgInfo.pmToBot || msgInfo.addressed) break;

                        target = msg.pName;

                        if (msgInfo.prmNum < 2)
                        {
                            reply.CreatePrivMsg(target, "Not enough parameters.");
                            send(reply);
                            break;
                        }

                        if (accdb.ContainsKey(msgInfo.prms[1]))
                        {

                            if (accdb[msgInfo.prms[1]].level == ircUserPriv.UNKNOWN)
                            {
                                reply.CreatePrivMsg(target, "Cannot further demote user, he is already IGNORED.");
                                send(reply);
                                break;
                            }

                            // user exists in acc db
                            ircUser nUser = accdb[msgInfo.prms[1]];

                            String rmKey = "invalid nickname";

                            if (users.ContainsValue(nUser))
                            {

                                Dictionary<String, ircUser>.Enumerator e = users.GetEnumerator();

                                do
                                {
                                    if (e.Current.Value.Equals(nUser)) { rmKey = e.Current.Key; break; }

                                } while (e.MoveNext());

                                if (!rmKey.Equals("invalid nickname")) users.Remove(rmKey);

                            }

                            uint tmpLevel = (uint)nUser.level;
                            tmpLevel--;
                            nUser.level = (ircUserPriv)tmpLevel;
                            accdb.Remove(nUser.nick);
                            accdb.Add(nUser.nick, nUser);
                            if (!rmKey.Equals("invalid nickname")) users.Add(rmKey, nUser);

                            reply.CreatePrivMsg(target, "User succesfully demoted to " + nUser.level.ToString() + ".");

                        }
                        else
                        {
                            reply.CreatePrivMsg(target, "No such user.");
                        }

                        send(reply);

                        break;
                    }
                case pmType.UPMSG:
                    {

                        if (msgInfo.pmToBot) target = msg.pName;
                        
                        if (msgInfo.addressed)
                        {
                            
                            if (msgInfo.prmNum < 2)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            StringBuilder sb = new StringBuilder();

                            for (int i = 2; i < msgInfo.prmNum; i++)
                            {
                                sb.Append(msgInfo.prms[i]).Append(" ");
                            }

                            upMessage = sb.ToString();
                            dumpServ();

                            reply.CreatePrivMsg(target, "Up topic succesfuly changed.");

                        }
                        else
                        {

                            if (msgInfo.prmNum < 1)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            StringBuilder sb = new StringBuilder();

                            for (int i = 1; i < msgInfo.prmNum; i++)
                            {
                                sb.Append(msgInfo.prms[i]).Append(" ");
                            }
                            
                            upMessage = sb.ToString();
                            dumpServ();

                            reply.CreatePrivMsg(target, "Up topic succesfuly changed.");

                        }

                        send(reply);
                        
                        break;
                    }
                case pmType.DOWNMSG:
                    {

                        if (msgInfo.pmToBot) target = msg.pName;

                        if (msgInfo.addressed)
                        {

                            if (msgInfo.prmNum < 2)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            StringBuilder sb = new StringBuilder();

                            for (int i = 2; i < msgInfo.prmNum; i++)
                            {
                                sb.Append(msgInfo.prms[i]).Append(" ");
                            }

                            downMessage = sb.ToString();
                            dumpServ();

                            reply.CreatePrivMsg(target, "Down topic succesfuly changed.");

                        }
                        else
                        {

                            if (msgInfo.prmNum < 1)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            StringBuilder sb = new StringBuilder();

                            for (int i = 1; i < msgInfo.prmNum; i++)
                            {
                                sb.Append(msgInfo.prms[i]).Append(" ");
                            }

                            downMessage = sb.ToString();
                            dumpServ();

                            reply.CreatePrivMsg(target, "Down topic succesfuly changed.");

                        }

                        send(reply);

                        break;
                    }
                case pmType.RAW:
                    {

                        if (!msgInfo.pmToBot || msgInfo.addressed) break;

                        target = msg.pName;

                        if (msgInfo.prmNum < 2)
                        {
                            reply.CreatePrivMsg(target, "You must specify what to send.");
                            send(reply);

                            break;
                        }

                        StringBuilder sb = new StringBuilder();

                        for (int i = 1; i < msgInfo.prmNum; i++)
                        {
                            sb.Append(msgInfo.prms[i]).Append(" ");
                        }

                        reply.Create(sb.ToString().Trim());
                        send(reply);
                        reply.CreatePrivMsg(target, "Succesfully sent.");
                        send(reply);

                        break;
                    }
                case pmType.HELP:
                    {
                        if (msgInfo.pmToBot) target = msg.pName;

                        reply.CreatePrivMsg(target, "http://moonshine.ceske-hry.cz/arthemis.txt");
                        send(reply);
                        break;
                    }
                case pmType.LCLOSE:
                    {

                        if (msgInfo.pmToBot) target = msg.pName;
                                                
                        if (msgInfo.addressed)
                        {

                            if (msgInfo.prmNum < 3)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            String phrase = msgInfo.prms[2].Replace("_", " ");
                            
                            if (alterLearn(phrase, 1))
                            {
                                reply.CreatePrivMsg(target, "Keyword locked.");
                            }
                            else
                            {
                                reply.CreatePrivMsg(target, "No such keyword.");
                            }
                        
                        }
                        else
                        {

                            if (msgInfo.prmNum < 2)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            String phrase = msgInfo.prms[1].Replace("_", " ");
                            
                            if (alterLearn(phrase, 1))
                            {
                                reply.CreatePrivMsg(target, "Keyword locked.");
                            }
                            else
                            {
                                reply.CreatePrivMsg(target, "No such keyword.");
                            }
                        
                        }

                        send(reply);
                        break;

                    }
                case pmType.LOPEN:
                    {

                        if (msgInfo.pmToBot) target = msg.pName;

                        if (msgInfo.addressed)
                        {

                            if (msgInfo.prmNum < 3)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            String phrase = msgInfo.prms[2].Replace("_", " ");

                            if (alterLearn(phrase, 0))
                            {
                                reply.CreatePrivMsg(target, "Keyword unlocked.");
                            }
                            else
                            {
                                reply.CreatePrivMsg(target, "No such keyword.");
                            }

                        }
                        else
                        {

                            if (msgInfo.prmNum < 2)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            String phrase = msgInfo.prms[1].Replace("_", " ");

                            if (alterLearn(phrase, 0))
                            {
                                reply.CreatePrivMsg(target, "Keyword unlocked.");
                            }
                            else
                            {
                                reply.CreatePrivMsg(target, "No such keyword.");
                            }

                        }

                        send(reply);
                        break;

                    }
                case pmType.LINFO:
                    {

                        if (msgInfo.pmToBot) target = msg.pName;
                        
                        if (msgInfo.addressed)
                        {

                            if (msgInfo.prmNum < 3)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            String phrase = msgInfo.prms[2].Replace("_", " ");

                            if (memory.ContainsKey(phrase))
                            {
                                StringBuilder answer = new StringBuilder();

                                answer.Append("I know ");
                                answer.Append(memory[phrase].numResponses.ToString());
                                answer.Append(" responses to this ");

                                if (memory[phrase].locked == 0)
                                {
                                    answer.Append("unlocked keyword. Ignore chance is: ");
                                }
                                else
                                {
                                    answer.Append("locked keyword. Ignore chance is: ");
                                }

                                answer.Append(memory[phrase].ignoreChance.ToString());
                                answer.Append(". First learned by ");
                                answer.Append(memory[phrase].author);
                                answer.Append(".");

                                reply.CreatePrivMsg(target, answer.ToString());
                            }
                            else
                            {
                                reply.CreatePrivMsg(target, "No such keyword.");
                            }

                        }
                        else
                        {

                            if (msgInfo.prmNum < 2)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            String phrase = msgInfo.prms[1].Replace("_", " ");

                            if (memory.ContainsKey(phrase))
                            {
                                StringBuilder answer = new StringBuilder();

                                answer.Append("I have ");
                                answer.Append(memory[phrase].numResponses.ToString());
                                answer.Append(" responses to this ");

                                if (memory[phrase].locked == 0)
                                {
                                    answer.Append("unlocked keyword. Ignore chance is: ");
                                }
                                else
                                {
                                    answer.Append("locked keyword. Ignore chance is: ");
                                }

                                answer.Append(memory[phrase].ignoreChance.ToString());
                                answer.Append(". First learned by ");
                                answer.Append(memory[phrase].author);
                                answer.Append(".");

                                reply.CreatePrivMsg(target, answer.ToString());
                            }
                            else
                            {
                                reply.CreatePrivMsg(target, "No such keyword.");
                            }

                        }

                        send(reply);
                        break;

                    }
                case pmType.IGNOREKZ:
                    {

                        if (msgInfo.pmToBot) target = msg.pName;

                        ignoreKZ = true;
                        reply.CreatePrivMsg(target, "Now ignoring users with access level below 2.");
                        send(reply);
                        break;
                    }
                case pmType.UNIGNOREKZ:
                    {

                        if (msgInfo.pmToBot) target = msg.pName;

                        ignoreKZ = false;
                        reply.CreatePrivMsg(target, "Users with access level below 2 are no longer ignored.");
                        send(reply);
                        break;
                    }
                case pmType.LIGNORE:
                    {
                        if (msgInfo.pmToBot) target = msg.pName;

                        if (msgInfo.addressed)
                        {

                            if (msgInfo.prmNum < 3)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            String phrase = msgInfo.prms[2].Replace("_", " ");

                            if (memory.ContainsKey(phrase))
                            {

                                memory[phrase].ignoreChance = 100;
                                memory[phrase].locked = 1;

                                reply.CreatePrivMsg(target, "Keyword is now locked and ignored.");
                            }
                            else
                            {
                                reply.CreatePrivMsg(target, "No such keyword.");
                            }

                        }
                        else
                        {

                            if (msgInfo.prmNum < 2)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            String phrase = msgInfo.prms[1].Replace("_", " ");

                            if (memory.ContainsKey(phrase))
                            {

                                memory[phrase].ignoreChance = 100;
                                memory[phrase].locked = 1;

                                reply.CreatePrivMsg(target, "Keyword is now locked and ignored.");
                            }
                            else
                            {
                                reply.CreatePrivMsg(target, "No such keyword.");
                            }

                        }

                        send(reply);
                        break;

                    }
                case pmType.LUNIGNORE:
                    {
                        if (msgInfo.pmToBot) target = msg.pName;

                        if (msgInfo.addressed)
                        {

                            if (msgInfo.prmNum < 3)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            String phrase = msgInfo.prms[2].Replace("_", " ");

                            if (memory.ContainsKey(phrase))
                            {                                                                

                                memory[phrase].ignoreChance = 5;
                                memory[phrase].locked = 0;
                                
                                reply.CreatePrivMsg(target, "Keyword has now default ignore chance of 5%.");
                            }
                            else
                            {
                                reply.CreatePrivMsg(target, "No such keyword.");
                            }

                        }
                        else
                        {

                            if (msgInfo.prmNum < 2)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            String phrase = msgInfo.prms[1].Replace("_", " ");

                            if (memory.ContainsKey(phrase))
                            {

                                memory[phrase].ignoreChance = 5;
                                memory[phrase].locked = 0;

                                reply.CreatePrivMsg(target, "Keyword has now default ignore chance of 5%.");
                            }
                            else
                            {
                                reply.CreatePrivMsg(target, "No such keyword.");
                            }

                        }

                        send(reply);
                        break;
                    }
                case pmType.LCHANCE:
                    {

                        if (msgInfo.pmToBot) target = msg.pName;

                        if (msgInfo.addressed)
                        {

                            if (msgInfo.prmNum < 4)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            String phrase = msgInfo.prms[2].Replace("_", " ");

                            if (memory.ContainsKey(phrase))
                            {

                                if (memory[phrase].locked == 1)
                                {
                                    reply.CreatePrivMsg(target, "Cannot change ignore chance, keyword is locked.");
                                    send(reply);
                                    break;
                                }

                                try
                                {
                                    int chance = Convert.ToInt32(msgInfo.prms[3]);

                                    if (chance < 0 || chance > 100) throw new ArgumentOutOfRangeException();

                                    memory[phrase].ignoreChance = chance;
                                }
                                catch (Exception)
                                {
                                    reply.CreatePrivMsg(target, "Wrong chance value. Nothing changed.");
                                    send(reply);
                                    break;
                                }
                                
                                reply.CreatePrivMsg(target, "Ignore chance succesfully changed.");
                            }
                            else
                            {
                                reply.CreatePrivMsg(target, "No such keyword.");
                            }

                        }
                        else
                        {

                            if (msgInfo.prmNum < 3)
                            {
                                reply.CreatePrivMsg(target, "Not enough parameters.");
                                send(reply);
                                break;
                            }

                            String phrase = msgInfo.prms[1].Replace("_", " ");

                            if (memory.ContainsKey(phrase))
                            {

                                if (memory[phrase].locked == 1)
                                {
                                    reply.CreatePrivMsg(target, "Cannot change ignore chance, keyword is locked.");
                                    send(reply);
                                    break;
                                }

                                try
                                {
                                    int chance = Convert.ToInt32(msgInfo.prms[2]);

                                    if (chance < 0 || chance > 100) throw new ArgumentOutOfRangeException();

                                    memory[phrase].ignoreChance = chance;
                                }
                                catch (Exception)
                                {
                                    reply.CreatePrivMsg(target, "Wrong chance value. Nothing changed.");
                                    send(reply);
                                    break;
                                }

                                reply.CreatePrivMsg(target, "Ignore chance succesfully changed.");
                            }
                            else
                            {
                                reply.CreatePrivMsg(target, "No such keyword.");
                            }

                        }

                        send(reply);
                        break;

                    }
                case pmType.STATS:
                    {
                        if (msgInfo.pmToBot) target = msg.pName;

                        StringBuilder answer = new StringBuilder();

                        answer.Append("I have ");
                        answer.Append(accdb.Count);
                        answer.Append(" users in database, ");
                        answer.Append(users.Count);
                        answer.Append(" are logged in. ");
                        answer.Append("Currently i know ");
                        answer.Append(memory.Count);
                        answer.Append(" keywords and ");

                        int resp = 0;

                        foreach (learnedItem l in memory.Values)
                        {
                            resp += l.numResponses;
                        }

                        answer.Append(resp);
                        answer.Append(" replies. There are ");
                        answer.Append(pmCmds.Count);
                        answer.Append(" commands available to you.");
                        
                        reply.CreatePrivMsg(target, answer.ToString());
                        send(reply);
                        break;

                    }
                case pmType.LLIST:
                    {

                        if (msgInfo.addressed) break;
                        if (memory.Count < 1) break;
                        if (msgInfo.pmToBot) target = msg.pName;
                        
                        String sPhrase;

                        if (msgInfo.prmNum > 1)
                        {
                            if (msgInfo.prms[1].Length > 6)
                            {
                                reply.CreatePrivMsg(target, "Search string too long.");
                                send(reply);
                                break;
                            }

                            sPhrase = msgInfo.prms[1].ToLower();

                        }
                        else
                        {
                            sPhrase = null;
                        }

                        StringBuilder answer = new StringBuilder();

                        foreach (String s in memory.Keys)
                        {

                            if (sPhrase != null)
                            {
                                if (s.StartsWith(sPhrase)) answer.Append("'").Append(s).Append("' ");
                            }
                            else
                            {
                                answer.Append("'").Append(s).Append("' ");
                            }

                            if (answer.Length > 350) break;
                        }
                                                
                        if (sPhrase != null)
                        {

                            if (answer.Length == 0)
                            {
                                answer.Insert(0, "'.");
                                answer.Insert(0, sPhrase);
                                answer.Insert(0, "I don't know any keywords beginning with '");
                            }
                            else
                            {
                                answer.Insert(0, "': ");
                                answer.Insert(0, sPhrase);
                                answer.Insert(0, "I know the following keywords beginning with '");
                            }
                                                        
                        }
                        else
                        {
                            if (answer.Length == 0)
                            {
                                answer.Insert(0, "I don't know any keywords.");
                            }
                            else
                            {
                                answer.Insert(0, "I know these keywords: ");
                            }
                        }

                        reply.CreatePrivMsg(target, answer.ToString());
                        send(reply);
                        break;
                    
                    }
                case pmType.SEEN:
                    {

                        if (msgInfo.addressed) break;
                        if (msgInfo.pmToBot) target = msg.pName;

                        if (msgInfo.prmNum < 2)
                        {
                            reply.CreatePrivMsg(target, "Not enough parameters.");
                            send(reply);
                            break;
                        }
                                                
                        String nick = msgInfo.prms[1].ToLower();

                        if (nick.Equals(msg.pName.ToLower()))
                        {
                            reply.CreatePrivMsg(target, "Seriously, you drink too much.");
                            send(reply);
                            break;
                        }

                        if (seenList.ContainsKey(nick))
                        {
                            reply.CreatePrivMsg(target, msgInfo.prms[1] + " was last seen on " + seenList[nick].ToString() + " .");
                        }
                        else
                        {
                            reply.CreatePrivMsg(target, "I have no idea :(");
                        }

                        send(reply);
                        break;

                    }
                
            }   
        
        }

        // 0 ok removed
        // 1 not removed, does not exist
        // 2 not removed, locked key
        public int remFromMemory(ircPrivMsg msg)
        {
            String nPhrase;
            
            if (msg.addressed)
            {
                // not enough params, return
                if (msg.prmNum < 3) return 1;

                nPhrase = msg.prms[2];

            }
            else
            {
                // not enough params, return
                if (msg.prmNum < 2) return 1;

                nPhrase = msg.prms[1];
                 
            }

            String temporary = nPhrase.Replace("_", " ");
            nPhrase = temporary.Trim().ToLower();

            if (memory.ContainsKey(nPhrase))
            {
                // key present, remove only if its not locked
                if (memory[nPhrase].locked == 1) return 2;

                memory.Remove(nPhrase);

            }
            else
            {
                // key does not exist
                return 1;
            }

            return 0;
        }

        public void dumpServ()
        {
                        
            String servFile = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"serv.txt";
            String servTempFile = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"serv_new.txt";

            TextWriter tw = new StreamWriter(servTempFile);
                        
            tw.WriteLine(upMessage);
            tw.WriteLine(downMessage);
            
            tw.Close();

            if (File.Exists(servFile))
            {

                File.Delete(servFile);
                File.Move(servTempFile, servFile);

            }
            else
            {
                File.Move(servTempFile, servFile);
            }

        }

        public void dumpSeenList()
        {

            String seenFile = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"seen.txt";
            String seenTempFile = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"seen_new.txt";

            TextWriter tw = new StreamWriter(seenTempFile);

            Dictionary<String, DateTime>.Enumerator e = seenList.GetEnumerator();

            e.MoveNext();

            while (true)
            {

                tw.WriteLine(e.Current.Key);
                tw.WriteLine(e.Current.Value.ToString());
                
                if (!e.MoveNext()) break;

                tw.WriteLine("# comment line");

            }                        

            tw.Close();

            if (File.Exists(seenFile))
            {

                File.Delete(seenFile);
                File.Move(seenTempFile, seenFile);

            }
            else
            {
                File.Move(seenTempFile, seenFile);
            }

        }

        public bool alterLearn(String phrase, int lckStatus)
        {

            if (memory.ContainsKey(phrase))
            {
                memory[phrase].locked = lckStatus;
                return true;
            }
            else
            {
                return false;
            }

        }

        // 0 - ok
        // 1 too few params
        // 2 too long key or value
        // 3 too short key
        // 4 cannot add, learn locked
        public int addToMemory(ircPrivMsg msg, String author)
        {
            String nPhrase;
            StringBuilder nResponse = new StringBuilder();

            if (msg.addressed)
            {
                // not enough params, return
                if (msg.prmNum < 4) return 1;

                nPhrase = msg.prms[2];

                for (int i = 3; i < msg.prmNum; i++)
                {
                    nResponse.Append(msg.prms[i]).Append(" ");
                }
                                
            }
            else
            {
                // not enough params, return
                if (msg.prmNum < 3) return 1;

                nPhrase = msg.prms[1];

                for (int i = 2; i < msg.prmNum; i++)
                {
                    nResponse.Append(msg.prms[i]).Append(" ");
                }

            }

            if (nResponse.Length > 400 || nPhrase.Length > 50) return 2;
            if (nPhrase.Length < 2) return 3;

            String temporary = nPhrase.Replace("_", " ");
            nPhrase = temporary.Trim().ToLower();

            if (memory.ContainsKey(nPhrase))
            {
                // key present, add only if its not locked
                if (memory[nPhrase].locked == 1) return 4;

                memory[nPhrase].addResponse(nResponse.ToString());
                
            }
            else
            {
                
                // key does not exist, create new
                learnedItem l = new learnedItem();
                l.ignoreChance = 5;
                l.locked = 0;
                l.author = author;
                l.addResponse(nResponse.ToString());

                memory.Add(nPhrase, l);
            }
            
            return 0;
        }

        public void dumpMemory()
        {

            if (memory.Count < 1) return;

            String memFile = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"memory.txt";
            String memTempFile = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"memory_new.txt";

            TextWriter tw = new StreamWriter(memTempFile);

            Dictionary<String, learnedItem>.Enumerator e = memory.GetEnumerator();

            e.MoveNext();

            while (true)
            {

                tw.WriteLine(e.Current.Key);
                tw.WriteLine(e.Current.Value.ignoreChance.ToString());
                tw.WriteLine(e.Current.Value.locked.ToString());
                tw.WriteLine(e.Current.Value.author);

                foreach (String s in e.Current.Value.responses)
                {
                    tw.WriteLine(s);
                }

                if (!e.MoveNext()) break;

                tw.WriteLine("# comment line");

            }

            tw.Close();

            if (File.Exists(memFile))
            {

                File.Delete(memFile);
                File.Move(memTempFile, memFile);

            }
            else
            {
                File.Move(memTempFile, memFile);
            }
                       

        }

        public void dumpAccDB()
        {

            if (accdb.Count < 1) return;

            String accFile = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"accdb.txt";
            String accTempFile = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"accdb_new.txt";

            TextWriter tw = new StreamWriter(accTempFile);

            Dictionary<String, ircUser>.Enumerator e = accdb.GetEnumerator();

            e.MoveNext();

            while (true)
            {

                tw.WriteLine(e.Current.Key);
                tw.WriteLine(e.Current.Value.pwd);
                tw.WriteLine((uint)e.Current.Value.level);
                                
                if (!e.MoveNext()) break;

                tw.WriteLine("# comment line");

            }

            tw.Close();

            if (File.Exists(accFile))
            {

                File.Delete(accFile);
                File.Move(accTempFile, accFile);

            }
            else
            {
                File.Move(accTempFile, accFile);
            }
        }

        public ircPrivMsg analyzePrivMsg(ircMessage msg)
        {
            
            ircPrivMsg result = new ircPrivMsg();

            result.sender.level = ircUserPriv.UNKNOWN;
            result.pmToBot = false;
            result.addressed = false;
            result.isCmd = false;
            result.cmd.type = pmType.NONE;
            result.cmd.reqLevel = ircUserPriv.MASTER;

            if (users.ContainsKey(msg.pName)) result.sender = users[msg.pName];

            // is it a pm directly to bot?
            if (msg.parameters[0].Contains(botNick)) result.pmToBot = true;
            // is it adressed to bot?
            if (msg.parameters[msg.prmCount - 1].ToLower().StartsWith(botNick.ToLower())) result.addressed = true;

            // split the text of PM for further analysis
            string[] delim = new string[] { " " };

            result.prms = msg.parameters[msg.prmCount - 1].Split(delim, 256, StringSplitOptions.RemoveEmptyEntries);
            result.prmNum = result.prms.GetLength(0);

            if (result.addressed)
            {
                // addressed, look for command in the second param
                // but only if there are at least 2 params
                if (result.prmNum > 1)
                {

                    // find out which command this is
                    foreach (pmCommand c in pmCmds)
                    {

                        if (result.prms[1].Contains(c.phrase))
                        {
                            result.isCmd = true;
                            result.cmd = c;
                            break;
                        }

                    }
                }

            }
            else
            {

                // not addressed, look for command in the first param
                // find out which command this is
                foreach (pmCommand c in pmCmds)
                {

                    if (result.prms[0].Contains(c.phrase))
                    {
                        result.isCmd = true;
                        result.cmd = c;
                        break;
                    }

                }
            
            }

            if (debugLevel == dbgLevel.HIGH)
            {
                Console.WriteLine("PRIVMSG analysis: pmToBot: {0}, addressed: {1}, cmd type: {2}, number of params: {3}", result.pmToBot.ToString(), result.addressed.ToString(), result.cmd.type.ToString(), result.prmNum.ToString());
            }
            
            return result;
        
        }
                
        public void setup()
        {

            rnd = new Random(27093);

            serverStatus = true;
            ignoreKZ = false;

            this.chat.Capacity = 500;
            this.run = true;
            this.conStatus = botConStatus.DISCONNECTED;

            String temp = null;
            String file = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"accdb.txt";

            Console.WriteLine("Attempting to read file: {0}", file);

            if (File.Exists(file))
            {

                Console.WriteLine("Success, reading contents...");
                // file exists
                TextReader tr = new StreamReader(file);
                ircUser usr;

                do
                {

                    //// id (string)
                    //temp = tr.ReadLine();
                    //usr.id = temp.Trim();
                    // nick (string)
                    temp = tr.ReadLine();
                    usr.nick = temp.Trim();
                    // password (string)
                    temp = tr.ReadLine();
                    usr.pwd = temp.Trim();
                    // access level (int)
                    temp = tr.ReadLine();
                    usr.level = (ircUserPriv)(Convert.ToInt32(temp.Trim()));

                    //users.Add(usr);
                    accdb.Add(usr.nick, usr);
                    //Console.WriteLine("- ADDED USER: id: {0} nick: {1} password: {2} level: {3}", usr.id, usr.nick, usr.pwd, usr.level.ToString());
                    Console.WriteLine("- ADDED ACCOUNT: nick: {0} password: {1} level: {2}", usr.nick, usr.pwd, usr.level.ToString());
                    // comment line
                    temp = tr.ReadLine();
                
                } while (temp != null);

                // close the stream
                tr.Close();
                Console.WriteLine("Done\n");
            }

            temp = null;
            file = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"commands.txt";
            Console.WriteLine("Attempting to read file: {0}", file);

            if (accdb.Count == 0)
            {
                // empty account database!
                // fill in default account
                ircUser usr;

                usr.nick = "master";
                usr.pwd = RijndaelSimple.quickEncrypt(cipherPass, "password");
                usr.level = ircUserPriv.MASTER;

                dumpAccDB();

                Console.WriteLine("Warning! No user found in Account Database. Default master:password account created.");

            }

            if (File.Exists(file))
            {

                Console.WriteLine("Success, reading contents...");
                // file exists
                TextReader tr = new StreamReader(file);
                pmCommand pCmd;

                do
                {

                    // id (string)
                    temp = tr.ReadLine();
                    pCmd.phrase = temp.Trim();
                    // nick (string)
                    temp = tr.ReadLine();
                    pCmd.reqLevel = (ircUserPriv)(Convert.ToInt32(temp.Trim()));
                    // password (string)
                    temp = tr.ReadLine();
                    pCmd.type = (pmType)(Convert.ToInt32(temp.Trim()));

                    pmCmds.Add(pCmd);
                    Console.WriteLine("- ADDED COMMAND: phrase: {0} req. level: {1} type: {2}", pCmd.phrase, pCmd.reqLevel.ToString(), pCmd.type.ToString());
                    // comment line
                    temp = tr.ReadLine();
                
                } while (temp != null);

                // close the stream
                tr.Close();
                Console.WriteLine("Done\n");
            
            }

            temp = null;
            file = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"memory.txt";
            Console.WriteLine("Attempting to read file: {0}", file);

            if (File.Exists(file))
            {

                Console.WriteLine("Success, reading contents...");
                // file exists
                TextReader tr = new StreamReader(file);
                learnedItem litem;

                String nPhrase;

                do
                {

                    litem = new learnedItem();

                    // phrase (string)
                    temp = tr.ReadLine();
                    nPhrase = temp.Trim();
                    // ignoreChance (int)
                    temp = tr.ReadLine();
                    litem.ignoreChance = Convert.ToInt32(temp.Trim());
                    // locked (bool)
                    temp = tr.ReadLine();
                    litem.locked = Convert.ToInt32(temp.Trim());
                    // author (String)
                    temp = tr.ReadLine();
                    litem.author = temp.Trim();

                    temp = tr.ReadLine();
                    do 
                    {
                        litem.addResponse(temp.Trim());
                        temp = tr.ReadLine();

                        if (temp == null) break;

                    } while (!temp.StartsWith("#"));

                    memory.Add(nPhrase, litem);

                    Console.WriteLine("- ADDED MEMMORY ITEM: phrase: {0} ignore chance: {1} number of responses: {2}", nPhrase, litem.ignoreChance.ToString(), litem.numResponses.ToString());
                    
                } while (temp != null);

                // close the stream
                tr.Close();
                Console.WriteLine("Done\n");

            }

            // ==============================================================================

            temp = null;
            file = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"seen.txt";
            Console.WriteLine("Attempting to read file: {0}", file);

            if (File.Exists(file))
            {

                Console.WriteLine("Success, reading contents...");
                // file exists
                TextReader tr = new StreamReader(file);
                
                DateTime when;
                String nick;

                do
                {                                       

                    // nick (String)
                    temp = tr.ReadLine();
                    nick = temp.Trim();
                    // time (DateTime)
                    temp = tr.ReadLine();
                    when = Convert.ToDateTime(temp.Trim());
                    
                    seenList.Add(nick, when);

                    Console.WriteLine("- ADDED SEEN LIST ITEM: nick: {0} time: {1}", nick, when.ToString());

                    temp = tr.ReadLine();

                } while (temp != null);

                // close the stream
                tr.Close();
                Console.WriteLine("Done\n");

            }

            // ==============================================================================

            temp = null;
            file = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"serv.txt";
            Console.WriteLine("Attempting to read file: {0}", file);

            if (File.Exists(file))
            {
                TextReader tr = new StreamReader(file);
                temp = tr.ReadLine();
                upMessage = temp.Trim();
                temp = tr.ReadLine();
                downMessage = temp.Trim();
                Console.WriteLine("Done\n");
            }
            else
            {
                upMessage = "Server je up.";
                downMessage = "Server je down.";
            }

            temp = null;
            file = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"settings.txt";
            Console.WriteLine("Attempting to read file: {0}", file);

            if (File.Exists(file))
            {
                TextReader tr = new StreamReader(file);
                
                temp = tr.ReadLine();
                channel = temp.Trim();
                temp = tr.ReadLine();
                botNick = temp.Trim();
                temp = tr.ReadLine();
                debugLevel = (dbgLevel)(Convert.ToUInt32(temp));
                
                Console.WriteLine("Done\n");
            }
            else
            {
                channel = "#gameparkl2";
                botNick = "Arthemis";
                debugLevel = dbgLevel.HIGH;
            }
            
        }

        public bool connect()
        {
            this.client.Connect(this.server, this.port);

            try
            {

                this.netStream = this.client.GetStream();

            }
            catch (Exception)
            {

                return false;
            }

            return true;

        }

        public void disconnect()
        {
            this.client.Close();
        }

        protected void send(ircMessage msg)
        {
            // used for output to the stream
            ASCIIEncoding asciiEnc = new ASCIIEncoding();
            byte[] output = asciiEnc.GetBytes(msg.rawMessage + "\r\n");

            this.netStream.Write(output, 0, output.Length);

            if (msg.type != iMsgType.PONG)
                Console.WriteLine("<- SENT: \t|{0}", msg.rawMessage);

        }

        protected void receive()
        {

            byte[] data = new byte[5000];
            int length = netStream.Read(data, 0, 5000);
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < length; i++)
                    sb.Append(Convert.ToChar(data[i]));

            String[] delim = new String[] { "\r\n" };
            String[] results = sb.ToString().Split(delim, StringSplitOptions.RemoveEmptyEntries);
            ircMessage result = new ircMessage();

            foreach (String s in results)
            {
                //result.Create(s);
                result = new ircMessage(s);
                this.newMsg.Enqueue(result);

                //Console.WriteLine("-> RECEIVED: \t|{0}", result.rawMessage);

                if (result.type != iMsgType.PING)
                    Console.WriteLine("-> RECEIVED: \t|{0}", result.Output());
            }

        }

    }

}